/*
  Copyright (c) 2002, 2003 Russell Morris
  All rights reserved.

  Redistribution and use in source and binary forms, with or without modification, 
  are permitted provided that the following conditions are met:

    * Redistributions of source code must retain the above copyright notice, 
      this list of conditions and the following disclaimer. 
      
    * Redistributions in binary form must reproduce the above copyright notice, 
      this list of conditions and the following disclaimer in the documentation 
      and/or other materials provided with the distribution. 
      
    * The names of the contributors may not be used to endorse or promote 
      products derived from this software without specific prior written permission. 
      
    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
    AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
    ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
    LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
    CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 
    IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
    THE POSSIBILITY OF SUCH DAMAGE.
*/

using System;
using System.Collections;
using System.Windows.Forms;

namespace WRM.Windows.Forms
{

    /// <summary>
    ///   EventArgs-derived class to describe events involving a <see cref="PaneNode"/>.
    /// </summary>
    internal class PaneNodeEventArgs : EventArgs
    {
        private PaneNode mPaneNode;

        /// <summary>
        ///   Construct a new instance of this class.
        /// </summary>
        /// <param name="paneNode">The <c>PaneNode</c> that is involved in this event</param>
        public PaneNodeEventArgs(PaneNode paneNode)
        {
            mPaneNode = paneNode;
        }

        /// <summary>
        ///   Gets the <see cref="PaneNode"/> that was involved in this event
        /// </summary>
        /// <value>
        ///   The <see cref="PaneNode"/> that was involved in this event
        /// </value>
        public PaneNode PaneNode
        {
            get
            {
                return mPaneNode;
            }
        }
    }
    /// <summary>
    ///   Delegate for events involving <see cref="PaneNode"/>s
    /// </summary>
    internal delegate void PaneNodeEventHandler(object sender, PaneNodeEventArgs e);
  
    /// <summary>
    /// Represents a collection of <see cref="PaneNode">PaneNode</see> objects.
    /// </summary>
    public sealed class PaneNodeCollection : IList, ICollection, IEnumerable
    {
        private ArrayList mList;
        private PaneNode mParent;

        /// <summary>
        /// Constructs a new instance of <c>PaneNodeCollection</c> with the
        /// given <see cref="PaneNode"/> <paramref name="parentPaneNode"/> as
        /// the parent of all <c>PaneNode</c>s in the collection.
        /// </summary>
        /// <param name="parentPaneNode">
        /// The <c>PaneNode</c> that acts as the parent to all <c>PaneNode</c>s
        /// in the collection
        /// </param>
        /// <remarks>
        /// This constructor is marked internal because <c>PaneNodeCollection</c>
        /// is designed to only be created as a part of the construction of a
        /// <see cref="PaneNode"/>
        /// </remarks>
        internal PaneNodeCollection(PaneNode parentPaneNode)
        {
            mList = new ArrayList();
            mParent = parentPaneNode;
        }

        /// <summary>
        /// Removes the given <paramref name="node"/> from the collection
        /// </summary>
        /// <param name="node">
        /// The <c>PaneNode</c> to remove
        /// </param>
        private void RemovePaneNodeFromList(PaneNode node)
        {
            mList.Remove(node);
            node.ParentInternal = null;

            // Fix up the indices of all children after the removed node
            BumpChildIndices(node,node.Index,false);
        }

        /// <summary>
        /// Inserts the given <paramref name="node"/> into the collection
        /// </summary>
        /// <param name="node">
        /// The <c>PaneNode</c> to insert
        /// </param>
        /// <exception cref="InvalidOperationException">
        /// Thrown if <paramref name="node"/> already has a valid
        /// <see cref="PaneNode.Parent">Parent</see>
        /// </exception>
        private void InsertPaneNodeIntoList(PaneNode node)
        {
            int insertIndex = node.Index;

            if(node.Parent != null)
            {
                if(!node.Parent.IsDummy)
                {
                    throw new InvalidOperationException(
                        "The PaneNode named '" + node.Text + "' cannot be added to this PaneCollection because it has already been added to another PaneCollection.");
                }
                else
                    node.ParentInternal = null;
            }

            // -1 index means add at end of list
            if(insertIndex == -1)
            {
                insertIndex = mList.Count;
                node.IndexInternal = insertIndex;
            }

            // Insert into list and fix-up child indices
            mList.Insert(insertIndex,node);
            BumpChildIndices(node,insertIndex,true);
            node.ParentInternal = Parent;
        }

        /// <summary>
        /// Re-assigns the internal indices of each child <c>PaneNode</c> so that
        /// they are all sequential
        /// </summary>
        /// <param name="node">
        /// The <c>PaneNode</c> that caused the indices to be out-of-whack.  This can
        /// be because it was added, or because it was removed.
        /// </param>
        /// <param name="index">
        /// The index at which the break ocurred
        /// </param>
        /// <param name="wasInserted">
        /// Boolean value indicating whether or not <paramref name="node"/> was
        /// inserted.
        /// </param>
        private void BumpChildIndices(PaneNode node, int index, bool wasInserted)
        {
            foreach(PaneNode childNode in mList)
            {
                if(childNode == node)
                    continue;

                if(childNode.Index >= index)
                    childNode.IndexInternal += wasInserted ? 1 : -1;
            }
        }

        /// <summary>
        /// Wrapper around <see cref="PropertyTree.GetSharedPaneInstance"/>
        /// </summary>
        /// <param name="sharedPaneType">
        /// The <see cref="Type"/> of <see cref="SharedPropertyPane"/> to get
        /// an instance of
        /// </param>
        /// <returns>
        /// An instance of the type <paramref name="sharedPaneType"/>
        /// </returns>
        private SharedPropertyPane GetSharedPaneInstance(Type sharedPaneType)
        {
            return Parent.PropertyTree.GetSharedPaneInstance(sharedPaneType);
        }

        /// <summary>
        /// Wrapper around <see cref="PropertyTree.ReleaseSharedPaneInstance"/>
        /// </summary>
        /// <param name="sharedPaneType">
        /// The <see cref="Type"/> of <see cref="SharedPropertyPane"/> to release
        /// an instance of
        /// </param>
        private void ReleaseSharedPaneInstance(Type sharedPaneType)
        {
            Parent.PropertyTree.ReleaseSharedPaneInstance(sharedPaneType);
        }

        /// <summary>
        /// Gets the <see cref="PaneNode">PaneNode</see> whose child <see cref="PaneNode"/>s are held by
        /// this collection
        /// </summary>
        /// <value>
        /// A reference to the <see cref="PaneNode"/> whose child <see cref="PaneNode"/>s are
        /// held in this collection.
        /// </value>
        public PaneNode Parent
        {
            get
            {
                return mParent;
            }
        }

    #region Implementation of ICollection
        /// <summary>
        /// Explicit implementation of <see cref="ICollection.CopyTo"/>
        /// </summary>
        /// <param name="array">
        /// The array to copy to
        /// </param>
        /// <param name="index">
        /// This index at which to begin copying
        /// </param>
        void ICollection.CopyTo(System.Array array, int index)
        {
            mList.CopyTo(array,index);
        }

        /// <summary>
        /// Copies the <see cref="PaneNode"/>s in this collection to the
        /// <c>PaneNodeCollection</c> <paramref name="other"/>
        /// </summary>
        /// <param name="other">
        /// The <c>PaneNodeCollection</c> to copy this <c>PaneNodeCollection</c>'s
        /// child nodes to
        /// </param>
        internal void CopyTo(PaneNodeCollection other)
        {
            foreach(PaneNode child in this)
            {
                other.Add(child);
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not this collection is synchronized
        /// </summary>
        /// <value>
        /// Returns a boolean value indicating whether or not this
        /// collection is synchronized
        /// </value>
        public bool IsSynchronized
        {
            get
            {
                return mList.IsSynchronized;
            }
        }

        /// <summary>
        /// Gets the number of <see cref="PaneNode">PaneNode</see>s in 
        /// the collection
        /// </summary>
        /// <value>
        /// An integer representing the number of <see cref="PaneNode"/>s in
        /// the collection
        /// </value>
        public int Count
        {
            get
            {
                return mList.Count;
            }
        }

        /// <summary>
        /// Gets the object used to synchronize access to this collection
        /// </summary>
        /// <value>
        /// The object used to synchronize access to this collection
        /// </value>
        public object SyncRoot
        {
            get
            {
                return mList.SyncRoot;
            }
        }
    #endregion

    #region Implementation of IEnumerable

        /// <summary>
        /// Gets an <see cref="IEnumerator"/> object that will enumerate through
        /// the child <see cref="PaneNode"/>s of this collection
        /// </summary>
        /// <returns>
        /// An <see cref="IEnumerator"/> object that will enumerate through
        /// the child <see cref="PaneNode"/>s of this collection
        /// </returns>
        public System.Collections.IEnumerator GetEnumerator()
        {
            return mList.GetEnumerator();
        }
    #endregion

    #region Implementation of IList
        /// <summary>
        /// Removes the <see cref="PaneNode"/> at the given
        /// <paramref name="index"/>
        /// </summary>
        /// <param name="index">
        /// The index of the<c>PaneNode</c> to remove
        /// </param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Thrown if there is no <see cref="PaneNode"/> at index
        /// <paramref name="index"/>
        /// </exception>
        public void RemoveAt(int index)
        {
            PaneNode node = this[index];

            if(node == null)
                throw new ArgumentOutOfRangeException(
                    "index",
                    index,
                    "No PaneNode exists at this index");

            RemovePaneNodeFromList(node);
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.Insert"/>
        /// </summary>
        /// <param name="index">not used</param>
        /// <param name="value">not used</param>
        /// <remarks>
        /// This is implemented explicitly so that it is hidden from normal
        /// use.  Instead of using this, a <see cref="PaneNode"/>'s 
        /// <see cref="PaneNode.Index"/> property should be set to the index
        /// at which it is to appear in the list.
        /// </remarks>
        /// <exception cref="NotSupportedException">
        /// Thrown whenever this function is called
        /// </exception>
        void IList.Insert(int index, object value)
        {
            throw new NotSupportedException(
                "Insert() is not supported.  Set PaneNode.Index and use Add() instead.");
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.Remove"/>
        /// </summary>
        /// <param name="value">
        /// The object to be removed from this list.
        /// </param>
        /// <remarks>
        /// This is implemented explicitly so that it is hidden from normal
        /// use.  The strongly typed version is for public use.
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="value"/> is not a <see cref="PaneNode"/>
        /// </exception>
        void IList.Remove(object value)
        {
            if(!(value is PaneNode))
                throw new ArgumentException(
                    "The PaneNodeCollection can only operate on PaneNodes",
                    "value");

            Remove((PaneNode)value);
        }

        /// <summary>
        /// Removes the given <paramref name="paneNode"/> from this
        /// collection
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> to remove from this collection
        /// </param>
        /// <remarks>
        /// Removing a <see cref="PaneNode"/> froma <c>PaneNodeCollection</c>
        /// means that that <c>PaneNode</c> now has no parent.  Once a 
        /// <c>PaneNode</c> has been removed from a <c>PaneNodeCollection</c>, 
        /// it can safely be added to another one.
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="paneNode"/> is not a member
        /// of this collection
        /// </exception>
        public void Remove(PaneNode paneNode)
        {
            if(!Contains(paneNode))
                throw new ArgumentException(
                    "Cannot remove PaneNode from collection because it is not a member of this collection",
                    "paneNode");

            RemovePaneNodeFromList(paneNode);
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.Contains"/>
        /// </summary>
        /// <param name="value">
        /// The value to look for in the collection
        /// </param>
        /// <returns>
        /// A boolean value indicating whether or not <paramref name="value"/>
        /// was found in this collection.
        /// </returns>
        /// <remarks>
        /// This is implemented explicitly so that it is hidden from normal use.  The
        /// strongly typed version is for public use.
        /// </remarks>
        bool IList.Contains(object value)
        {
            return Contains((PaneNode)value);
        }

        /// <summary>
        /// Determines whether or not the given <see cref="PaneNode"/>
        /// <paramref name="paneNode"/> exists in this collection
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> to look for in this collection
        /// </param>
        /// <returns>
        /// A boolean value indicating whether or not <paramref name="paneNode"/>
        /// was found in this collection
        /// </returns>
        public bool Contains(PaneNode paneNode)
        {
            return mList.Contains(paneNode);
        }

        /// <summary>
        /// Clears the <c>PaneNodeCollection</c> of all <see cref="PaneNode"/>s
        /// </summary>
        public void Clear()
        {
            PaneNode[] nodes = new PaneNode[mList.Count];
            mList.CopyTo(nodes);

            foreach(PaneNode node in nodes)
                Remove(node);
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.IndexOf"/>
        /// </summary>
        /// <param name="value">
        /// The object whose index is to be determined
        /// </param>
        /// <returns>
        /// Returns the index of <paramref name="value"/> in 
        /// this collection, or <c>-1</c> if <paramref name="value"/>
        /// is not a member of this collection
        /// </returns>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="value"/> is not a <see cref="PaneNode"/>
        /// </exception>
        int IList.IndexOf(object value)
        {
            if(!(value is PaneNode))
                throw new ArgumentException(
                    "The PaneNodeCollection can only operate on PaneNodes",
                    "value");

            return IndexOf((PaneNode)value);
        }

        /// <summary>
        /// Gets the integer index for the <see cref="PaneNode"/> <paramref name="paneNode"/>
        /// </summary>
        /// <param name="paneNode">
        /// The <c>PaneNode</c> whose index is to be determined
        /// </param>
        /// <returns>
        /// Returns the index of <paramref name="paneNode"/> in 
        /// this collection, or <c>-1</c> if <paramref name="paneNode"/> is
        /// not a member of this collection
        /// </returns>
        public int IndexOf(PaneNode paneNode)
        {
            return mList.IndexOf(paneNode);
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.Add"/>
        /// </summary>
        /// <param name="value">
        /// The object to add to the collection
        /// </param>
        /// <returns>
        /// Returns the integer index of the newly added object
        /// </returns>
        /// <remarks>
        /// This is implemented explicitly so that it is hidden from general
        /// use.  The strongly typed version is for public use.
        /// </remarks>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="value"/> is not a <see cref="PaneNode"/>
        /// </exception>
        int IList.Add(object value)
        {
            if(!(value is PaneNode))
                throw new ArgumentException(
                    "The PaneNodeCollection can only operate on PaneNodes",
                    "value");

            return Add((PaneNode)value).Index;
        }

        /// <overloads>
        /// Each of the <c>Add</c> methods ends up adding a <see cref="PaneNode"/> to the collection
        /// of <see cref="PaneNode"/>s.
        /// </overloads>
        /// <summary>
        /// Adds the given <paramref name="paneNode"/> to this collection.
        /// </summary>
        /// <param name="paneNode">The <see cref="PaneNode">PaneNode</see> to add</param>
        /// <returns>
        /// <para>
        /// Returns the <see cref="PaneNode"/> object that was just added.  In this case,
        /// of course, this is the same object referenced by <paramref name="paneNode"/>.
        /// </para>
        /// <para>
        /// The return value was kept the same as the other overloads of <c>Add</c> for
        /// the sake of consistency.
        /// </para>
        /// </returns>
        /// <remarks>
        /// <para>
        /// When a new <see cref="PaneNode">PaneNode</see> is added to the collection, it and 
        /// all of its children are added to the tree if the 
        /// <see cref="PaneNode">PaneNode</see> that owns this <c>PaneNodeCollection</c> 
        /// is currently in the tree.
        /// </para>
        /// <para>
        /// The <see cref="PaneNode">PaneNode</see> referenced by 
        /// <paramref name="paneNode"/> must not already by contained in another 
        /// <c>PaneNodeCollection</c>.  If <paramref name="paneNode"/>
        /// is contained in another <c>PaneNodeCollection</c>, an exception is thrown.
        /// </para>
        /// </remarks>
        /// <exception cref="System.InvalidOperationException">
        ///   <paramref name="paneNode">paneNode</paramref> is currently assigned to 
        ///   a <c>PaneNodeCollection</c>.  A <see cref="PaneNode">PaneNode</see> can 
        ///   only be assigned to one <c>PaneNodeCollection</c> at a time.
        /// </exception>
        public PaneNode Add(PaneNode paneNode)
        {
            InsertPaneNodeIntoList(paneNode);
            return paneNode;
        }

        /// <summary>
        /// Creates a <see cref="PaneNode"/> to represent <paramref name="pane"/>, and adds it
        /// to the collection
        /// </summary>
        /// <param name="pane">
        /// The <see cref="PropertyPane"/> for which to add a <see cref="PaneNode"/>
        /// </param>
        /// <returns>
        /// <para>
        /// Returns the <see cref="PaneNode"/> object that was just added.
        /// </para>
        /// </returns>
        /// <remarks>
        /// <para>
        /// The versions of <c>Add</c> that take <see cref="PropertyPane"/>s as parameters
        /// are not intended to be used by anything other than designer-generated code
        /// (like that found in <c>InitializeComponent</c>).  It is a little bit of a
        /// hack that allows the <see cref="PropertyTree"/> to not have to serialize
        /// <see cref="PaneNode"/>s during design-time.
        /// </para>
        /// <para>
        /// This implementation is a shortcut that specifies default values of <c>-1</c>
        /// for <see cref="PaneNode.Index"/>, <see cref="PaneNode.ImageIndex"/>, and
        /// <see cref="PaneNode.SelectedImageIndex"/>
        /// </para>
        /// <para>
        /// When a new <see cref="PaneNode">PaneNode</see> is added to the collection, it and 
        /// all of its children are added to the tree if the 
        /// <see cref="PaneNode">PaneNode</see> that owns this <c>PaneNodeCollection</c> 
        /// is currently in the tree.
        /// </para>
        /// <para>
        /// The <see cref="PropertyPane">PropertyPane</see> <paramref name="pane"/>
        ///  must not already have a valid <see cref="PaneNode"/> associated with it.
        ///  If <paramref name="pane"/> already has a <c>PaneNode</c> associated
        ///  with it, an exception is thrown.
        /// </para>
        /// <para>
        /// Instances of classes derived from <see cref="SharedPropertyPane"/> cannot be
        /// added.  Use one of the other overloads to do that.
        /// </para>
        /// </remarks>
        /// <exception cref="System.InvalidOperationException">
        ///   Thrown if <paramref name="pane"/> already has a <see cref="PaneNode"/>
        ///   associated with it.
        /// </exception>
        public PaneNode Add(PropertyPane pane)
        {
            return Add(pane,-1,-1,-1);
        }

        /// <summary>
        /// Creates a <see cref="PaneNode"/> to represent <paramref name="pane"/>, and adds it
        /// to the collection
        /// </summary>
        /// <param name="pane">
        /// The <see cref="PropertyPane"/> for which to add a <see cref="PaneNode"/>
        /// </param>
        /// <param name="index">
        /// The index at which to add <paramref name="pane"/>
        /// </param>
        /// <param name="imageIndex">
        /// The default image index for <paramref name="pane"/>
        /// </param>
        /// <param name="selectedImageIndex">
        /// The default selected image index for <paramref name="pane"/>
        /// </param>
        /// <returns>
        /// <para>
        /// Returns the <see cref="PaneNode"/> object that was just added.
        /// </para>
        /// </returns>
        /// <remarks>
        /// <para>
        /// The versions of <c>Add</c> that take <see cref="PropertyPane"/>s as parameters
        /// are not intended to be used by anything other than designer-generated code
        /// (like that found in <c>InitializeComponent</c>).  It is a little bit of a
        /// hack that allows the <see cref="PropertyTree"/> to not have to serialize
        /// <see cref="PaneNode"/>s during design-time.
        /// </para>
        /// <para>
        /// When a new <see cref="PaneNode">PaneNode</see> is added to the collection, it and 
        /// all of its children are added to the tree if the 
        /// <see cref="PaneNode">PaneNode</see> that owns this <c>PaneNodeCollection</c> 
        /// is currently in the tree.
        /// </para>
        /// <para>
        /// The <see cref="PropertyPane">PropertyPane</see> <paramref name="pane"/>
        ///  must not already have a valid <see cref="PaneNode"/> associated with it.
        ///  If <paramref name="pane"/> already has a <c>PaneNode</c> associated
        ///  with it, an exception is thrown.
        /// </para>
        /// <para>
        /// Instances of classes derived from <see cref="SharedPropertyPane"/> cannot be
        /// added.  Use one of the other overloads to do that.
        /// </para>
        /// </remarks>
        /// <exception cref="System.InvalidOperationException">
        ///   Thrown if <paramref name="pane"/> already has a <see cref="PaneNode"/>
        ///   associated with it.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="pane"/> is derived from 
        /// <see cref="SharedPropertyPane"/>
        /// </exception>
        public PaneNode Add(
            PropertyPane pane,
            int index,
            int imageIndex,
            int selectedImageIndex)
        {
            if(typeof(SharedPropertyPane).IsAssignableFrom(pane.GetType()))
                throw new ArgumentException(
                    "You cannot add a SharedPropertyPane instance.  You must instead specify " +
                    "the System.Type of your SharedPropertyPane in one of the other " +
                    "PaneNodeCollection.Add() method overloads.",
                    "pane");

            PaneNode paneNode = new PaneNode(pane);
            paneNode.IndexInternal = index;
            paneNode.ImageIndex = imageIndex;
            paneNode.SelectedImageIndex = selectedImageIndex;

            InsertPaneNodeIntoList(paneNode);

            return paneNode;
        }

    
        /// <summary>
        ///   Adds a <see cref="PaneNode"/> that references a 
        ///   <see cref="SharedPropertyPane"/>
        /// </summary>
        /// <remarks>
        /// Adds a new <see cref="PaneNode"/> to the tree that represents an 
        /// instance of the <see cref="SharedPropertyPane"/> subclass identified by 
        /// <paramref name="sharedPaneType">sharedPaneType</paramref>. Only one instance 
        /// of <paramref name="sharedPaneType">sharedPaneType</paramref> is ever created, 
        /// so all shared panes added to the <see cref="PropertyTree"/> will share that 
        /// single instance of  <paramref name="sharedPaneType">sharedPaneType</paramref>.
        /// </remarks>
        /// <param name="sharedPaneType">
        /// The <c>Type</c> of the <see cref="SharedPropertyPane"/> subclass that will be used 
        /// for the new node</param>
        /// <param name="title">
        /// The <see cref="PaneNode.Text">Text</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <param name="index">
        /// The <see cref="PaneNode.Index">Index</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <param name="imageIndex">
        /// The <see cref="PaneNode.ImageIndex">ImageIndex</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <param name="selectedImageIndex">
        /// The <see cref="PaneNode.SelectedImageIndex">SelectedImageIndex</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <param name="data">
        /// The <see cref="PaneNode.Data">Data</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <returns>
        /// Returns the <see cref="PaneNode"/> that identifies the newly added 
        /// instance of <paramref name="sharedPaneType"/>
        /// </returns>
        /// <exception cref="ArgumentException">
        /// Thrown if <paramref name="sharedPaneType"/> is not a <c>Type</c>
        /// that descends from <see cref="SharedPropertyPane"/>
        /// </exception>
        public PaneNode Add(
            Type sharedPaneType,
            string title,
            int index,
            int imageIndex,
            int selectedImageIndex,
            object data)
        {
            if(!sharedPaneType.IsSubclassOf(typeof(SharedPropertyPane)))
                throw new ArgumentException(
                    "The type '" + sharedPaneType.FullName + "' " +
                    "is not a subclass of SharedPropertyPane.",
                    "sharedPaneType");

            PaneNode paneNode = new PaneNode(
                sharedPaneType,
                data);
            paneNode.Text = title;
            paneNode.IndexInternal = index;
            paneNode.ImageIndex = imageIndex;
            paneNode.SelectedImageIndex = selectedImageIndex;
      
            InsertPaneNodeIntoList(paneNode);

            return paneNode;
        }

        /// <summary>
        ///   Adds a <see cref="PaneNode"/> that references a 
        ///   <see cref="SharedPropertyPane"/>
        /// </summary>
        /// <remarks>
        /// <para>
        /// This implementation is a shortcut for the other implementation of 
        /// <c>Add</c> that deals with <see cref="SharedPropertyPane"/>s.  This
        /// version sets <see cref="PaneNode.Index"/>, <see cref="PaneNode.ImageIndex"/>, 
        /// and <see cref="PaneNode.SelectedImageIndex"/> of the generated
        /// <see cref="PaneNode"/> to the default values of <c>-1</c>.
        /// </para>
        /// <para>
        /// Adds a new <see cref="PaneNode"/> to the tree that represents an 
        /// instance of the <see cref="SharedPropertyPane"/> subclass identified by 
        /// <paramref name="sharedPaneType">sharedPaneType</paramref>. Only one instance 
        /// of <paramref name="sharedPaneType">sharedPaneType</paramref> is ever created, 
        /// so all shared panes added to the <see cref="PropertyTree"/> will share that 
        /// single instance of  <paramref name="sharedPaneType">sharedPaneType</paramref>.
        /// </para>
        /// </remarks>
        /// <param name="sharedPaneType">
        /// The <c>Type</c> of the <see cref="SharedPropertyPane"/> subclass that will be used 
        /// for the new node</param>
        /// <param name="title">
        /// The <see cref="PaneNode.Text">Text</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <param name="data">
        /// The <see cref="PaneNode.Data">Data</see> of the new 
        /// <see cref="PaneNode"/> that will be generated to represent
        /// the instance of <paramref name="sharedPaneType"/>
        /// </param>
        /// <returns>
        /// Returns the <see cref="PaneNode"/> that identifies the newly added 
        /// instance of <paramref name="sharedPaneType"/>
        /// </returns>
        public PaneNode Add(
            Type sharedPaneType,
            string title,
            object data)
        {
            return Add(sharedPaneType,title,-1,-1,-1,data);
        }

        /// <summary>
        /// Gets a boolean value indicating whether or not this
        /// collection is read only.
        /// </summary>
        /// <value>
        /// Always <c>false</c> for <c>PaneNodeCollection</c>
        /// </value>
        public bool IsReadOnly
        {
            get
            {
                return false;
            }
        }

        /// <summary>
        /// Explicit implementation of <see cref="IList.this"/>
        /// </summary>
        /// <param name="index">
        /// The index of the object to retreive
        /// </param>
        /// <value>
        /// The object at index <paramref name="index"/>, or 
        /// null if no object exists at that index.
        /// </value>
        /// <exception cref="NotSupportedException">
        /// Thrown if this method is used to attempt to set a value
        /// </exception>
        object IList.this[int index]
        {
            get
            {
                return mList[index];
            }
            set
            {
                throw new NotSupportedException("Setting via indexer is not supported for PaneNodeCollection");
            }
        }

        /// <summary>
        /// Gets the <see cref="PaneNode"/> at index <paramref name="index"/> in
        /// the collection, or <c>null</c> if no <c>PaneNode</c> exists at that location.
        /// </summary>
        /// <param name="index">
        /// The index of the <see cref="PaneNode"/> to retreive
        /// </param>
        /// <value>
        /// The <see cref="PaneNode"/> at index <paramref name="index"/> in
        /// the collection, or <c>null</c> if no <c>PaneNode</c> exists at that location.
        /// </value>
        public PaneNode this[int index]
        {
            get
            {
                return (PaneNode)mList[index];
            }
        }

        /// <summary>
        /// Gets a boolean value indicating whether or not this collection
        /// is of fixed size.
        /// </summary>
        /// <value>
        /// Always <c>false</c>
        /// </value>
        public bool IsFixedSize
        {
            get
            {
                return false;
            }
        }
    #endregion
    }
}
